// src/utils/jwtToken.ts

import jwt from 'jsonwebtoken'
import { User } from '../models/user'
import { Context, Next } from 'koa'
import { Secret, SignOptions } from 'jsonwebtoken'

// 自定义密钥
const secret: Secret = 'fl.b-18#cd_/12*+chan&koa^!.nam$milo'

// Token黑名单 - 在实际应用中应该使用Redis等外部存储
const tokenBlacklist = new Set<string>()

// 白名单 - 不需要token验证的路由
const whiteList = [
  '/api/v1/users',
  '/api/v1/users/login',
  '/api/v1/users/logout',
  '/api/v1/common/dictionary'
]

/**
 * 生成 token
 * @param data 加密的数据，最好不要包括密码之类的
 * @param time 有效时长，数字 1000 表示 1000s; 字符串 '10h' 表示 10小时，'1d' 表示1天；具体参考文档
 */
const signToken = (
  data: string | object | Buffer,
  time: SignOptions['expiresIn'] = '30d'
) => {
  const token = jwt.sign(data, secret, { expiresIn: time })
  return token
}

// 验证 token
const verifyToken = async (ctx: Context, next: Next) => {
  const url = ctx.request.url.split('?')[0]
  const method = ctx.request.method

  // 检查是否在白名单中，并考虑HTTP方法
  if (whiteList.includes(url)) {
    // 特殊处理 /api/v1/users 路径
    if (url === '/api/v1/users') {
      // POST 请求用于注册，无需token验证
      if (method === 'POST') {
        return next()
      }
      // 其他方法(GET, PUT, DELETE等)需要token验证
    } else {
      // 其他白名单路径无需token验证
      return next()
    }
  }

  const authHeader = ctx.request.header?.authorization
  if (!authHeader) {
    ctx.status = 401
    ctx.body = {
      success: false,
      message: '未提供认证信息'
    }
    return
  }

  const tokenParts = authHeader.split(' ')
  if (tokenParts.length !== 2 || tokenParts[0] !== 'Bearer') {
    ctx.status = 401
    ctx.body = {
      success: false,
      message: '认证信息格式错误'
    }
    return
  }

  const token = tokenParts[1]
  if (!token) {
    ctx.status = 401
    ctx.body = {
      success: false,
      message: '认证信息格式错误'
    }
    return
  }

  // 检查token是否在黑名单中
  if (tokenBlacklist.has(token)) {
    ctx.status = 401
    ctx.body = {
      success: false,
      message: 'token已失效，请重新登录'
    }
    return
  }

  try {
    const decoded = jwt.verify(token, secret) as any

    // 检查用户是否存在且token未过期
    const user = await User.findOne({ id: decoded.id })
    if (!user) {
      ctx.status = 401
      ctx.body = {
        success: false,
        message: '用户不存在'
      }
      return
    }

    // 检查用户最后登录时间是否与token创建时间一致
    // 这用于处理用户重新登录后旧token失效的情况
    if (user.updated_at !== decoded.token_created_at) {
      ctx.status = 401
      ctx.body = {
        success: false,
        message: 'token已过期，请重新登录'
      }
      return
    }

    // 将用户信息添加到上下文
    ctx.state.user = user
    return next()
  } catch (error) {
    ctx.status = 401
    ctx.body = {
      success: false,
      message: 'token无效或已过期'
    }
    return
  }
}

// 将token加入黑名单
const addToBlacklist = (token: string) => {
  tokenBlacklist.add(token)
}

// 检查token是否在黑名单中
const isTokenBlacklisted = (token: string) => {
  return tokenBlacklist.has(token)
}

export { signToken, verifyToken, secret, addToBlacklist, isTokenBlacklisted }